package slicegen;

import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;

import slicegen.Polygon.WindingDirection;

public class Line extends Line2D.Double
{
	public Line()
	{
		super();		
	}

	public Line(double X1, double Y1, double X2, double Y2)
	{
		super(X1, Y1, X2, Y2);
	}

	public Line(Point2D p1, Point2D p2)
	{
		super(p1, p2);
	}

	public Line(Point2D p1, Point2D p2, WindingDirection windDir)
	{
		super(p1, p2);

		this.windingDirection = windDir;
	}

	@Override
	public boolean equals(Object obj)
	{
		if (obj != null && obj instanceof Line)
		{
			Line other = (Line) obj;

			return other.getP1().equals(getP1()) && other.getP2().equals(getP2());
		}

		return super.equals(obj);
	}

	@Override
	public int hashCode()
	{
		//Use the same method used in Point2D
		long hashcode = getP1().hashCode();
		hashcode ^= getP2().hashCode() * 31;

		return (int) (hashcode ^ (hashcode >> 31));
	}

	public double length()
	{
		return getP1().distance(getP2());
	}

	public WindingDirection getWindingDirection()
	{
		return windingDirection;
	}

	public void setWindingDirection(WindingDirection windingDirection)
	{
		this.windingDirection = windingDirection;
	}

	@Override
	public String toString()
	{
		return getP1() + "->" + getP2();
	}

	private WindingDirection windingDirection;
	public Ray p1Bisector = null, p2Bisector = null;

	public void associateBisector(Ray bisector)
	{
		p1Bisector = bisector;
	}

	public void fitToBisectors()
	{
		if (p1Bisector == null || p2Bisector == null)
			//throw new RuntimeException("Not two bisectors");
			return;

		Point2D i1 = p1Bisector.intersects(new Ray(this));
		Point2D i2 = p2Bisector.intersects(new Ray(this));

		//I'm not sure when these are null...
		if (i1 == null)
			i1 = getP1();

		if (i2 == null)
			i2 = getP2();

		setLine(i1, i2);
	}

	public void swapEndpoints()
	{
		setLine(getP2(), getP1());

		Ray tBisector = p1Bisector;
		p1Bisector = p2Bisector;
		p2Bisector = tBisector;
	}

	public void associateBisector(Ray bisector, Point2D origin)
	{
		if (origin.equals(getP1()))
			p1Bisector = bisector;
		else if (origin.equals(getP2()))
			p2Bisector = bisector;
		else
			throw new RuntimeException("Passed origin is not on the line");
	}

	public Line offset(Ray r)
	{
		Point2D start = r.getOrigin(), end = r.getEndpoint();

		double xDist = end.getX() - start.getX();
		double yDist = end.getY() - start.getY();

		AffineTransform offsetTrans = AffineTransform.getTranslateInstance(xDist, yDist);

		Line oLine = (Line) clone();
		oLine.setLine(offsetTrans.transform(getP1(), null), offsetTrans.transform(getP2(), null));

		Point2D bisectorIsect = p1Bisector.intersects(p2Bisector);
		if (bisectorIsect != null)
		{
			double bisectDist = ptLineDist(bisectorIsect);

			//If WindingDirection is CCW then there can be invalid lines
			//Lines are invalid if they are past the intersection 
			if (windingDirection.equals(Polygon.WindingDirection.CCW) && bisectDist <= r.getRayLength())
			{
				//Line is invalid (it disappeared)
				return null;
			}
		}

		return oLine;
	}

	//Move the line's endpoint that is closest to pt to be that point
	public void extendTo(Point2D pt)
	{
		double distP1 = pt.distanceSq(getP1());
		double distP2 = pt.distanceSq(getP2());

		if (distP1 < distP2)
		{
			setLine(pt, getP2());
		}
		else
		{
			setLine(getP1(), pt);
		}
	}
}
